import numpy as np
import pandas as pd
import scipy
import matplotlib.pyplot as plt, seaborn as sns
import os
import librosa, librosa.display
import IPython.display as ipd
plt.rcParams['figure.figsize'] = (10, 3)
from sklearn import preprocessing
from scipy.spatial.distance import cdist[실험] (GTZAN) 노래 유사도 거리 측정
[Experiment] (GTZAN) Song Similarity with Distance Metrics
유사성
거리
GTZAN
Distance metric을 기반으로 노래의 유사성을 측정하고 가장 가까운 노래와 가장 먼 노래를 살펴보는 간단한 실험이다. 오디오 피쳐를 사용하여 측정한다.
데이터
GTZAN 데이터를 사용한다. 이미 추출된 피쳐를 불러온다. 총 1000곡이 있다.
# Read data
data = pd.read_csv('data/GTZAN_data/features_30_sec.csv')features = data.iloc[:,2:-1]
features_scaled=preprocessing.scale(features)features| chroma_stft_mean | chroma_stft_var | rms_mean | rms_var | spectral_centroid_mean | spectral_centroid_var | spectral_bandwidth_mean | spectral_bandwidth_var | rolloff_mean | rolloff_var | ... | mfcc16_mean | mfcc16_var | mfcc17_mean | mfcc17_var | mfcc18_mean | mfcc18_var | mfcc19_mean | mfcc19_var | mfcc20_mean | mfcc20_var | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0.350088 | 0.088757 | 0.130228 | 0.002827 | 1784.165850 | 129774.064525 | 2002.449060 | 85882.761315 | 3805.839606 | 9.015054e+05 | ... | 0.752740 | 52.420910 | -1.690215 | 36.524071 | -0.408979 | 41.597103 | -2.303523 | 55.062923 | 1.221291 | 46.936035 |
| 1 | 0.340914 | 0.094980 | 0.095948 | 0.002373 | 1530.176679 | 375850.073649 | 2039.036516 | 213843.755497 | 3550.522098 | 2.977893e+06 | ... | 0.927998 | 55.356403 | -0.731125 | 60.314529 | 0.295073 | 48.120598 | -0.283518 | 51.106190 | 0.531217 | 45.786282 |
| 2 | 0.363637 | 0.085275 | 0.175570 | 0.002746 | 1552.811865 | 156467.643368 | 1747.702312 | 76254.192257 | 3042.260232 | 7.840345e+05 | ... | 2.451690 | 40.598766 | -7.729093 | 47.639427 | -1.816407 | 52.382141 | -3.439720 | 46.639660 | -2.231258 | 30.573025 |
| 3 | 0.404785 | 0.093999 | 0.141093 | 0.006346 | 1070.106615 | 184355.942417 | 1596.412872 | 166441.494769 | 2184.745799 | 1.493194e+06 | ... | 0.780874 | 44.427753 | -3.319597 | 50.206673 | 0.636965 | 37.319130 | -0.619121 | 37.259739 | -3.407448 | 31.949339 |
| 4 | 0.308526 | 0.087841 | 0.091529 | 0.002303 | 1835.004266 | 343399.939274 | 1748.172116 | 88445.209036 | 3579.757627 | 1.572978e+06 | ... | -4.520576 | 86.099236 | -5.454034 | 75.269707 | -0.916874 | 53.613918 | -4.404827 | 62.910812 | -11.703234 | 55.195160 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 995 | 0.352063 | 0.080487 | 0.079486 | 0.000345 | 2008.149458 | 282174.689224 | 2106.541053 | 88609.749506 | 4253.557033 | 1.222421e+06 | ... | 1.789867 | 45.050526 | -13.289984 | 41.754955 | 2.484145 | 36.778877 | -6.713265 | 54.866825 | -1.193787 | 49.950665 |
| 996 | 0.398687 | 0.075086 | 0.076458 | 0.000588 | 2006.843354 | 182114.709510 | 2068.942009 | 82426.016726 | 4149.338328 | 1.046621e+06 | ... | 3.739020 | 33.851742 | -10.848309 | 39.395096 | 1.881229 | 32.010040 | -7.461491 | 39.196327 | -2.795338 | 31.773624 |
| 997 | 0.432142 | 0.075268 | 0.081651 | 0.000322 | 2077.526598 | 231657.968040 | 1927.293153 | 74717.124394 | 4031.405321 | 8.042154e+05 | ... | 1.838090 | 33.597008 | -12.845291 | 36.367264 | 3.440978 | 36.001110 | -12.588070 | 42.502201 | -2.106337 | 29.865515 |
| 998 | 0.362485 | 0.091506 | 0.083860 | 0.001211 | 1398.699344 | 240318.731073 | 1818.450280 | 109090.207161 | 3015.631004 | 1.332712e+06 | ... | -2.812176 | 46.324894 | -4.416050 | 43.583942 | 1.556207 | 34.331261 | -5.041897 | 47.227180 | -3.590644 | 41.299088 |
| 999 | 0.358401 | 0.085884 | 0.054454 | 0.000336 | 1609.795082 | 422203.216152 | 1797.213044 | 120115.632927 | 3246.908930 | 1.753476e+06 | ... | 1.794104 | 59.167755 | -7.069775 | 73.760391 | 0.028346 | 76.504326 | -2.025783 | 72.189316 | 1.155239 | 49.662510 |
1000 rows × 57 columns
거리 유사성 측정
def songs_similarity(song_name, features, metric='cosine'):
distances = cdist(features, features, metric=metric)
dist_df = pd.DataFrame(distances)
dist_df = dist_df.set_index(data.filename)
dist_df.columns = data.filename
series = dist_df[song_name].sort_values(ascending = True)
series = series.drop(song_name)
return series코사인 거리 기반 (based on cosine distance)
distances = cdist(features_scaled, features_scaled, 'cosine') #cosine distancedist_df = pd.DataFrame(distances)
dist_df = dist_df.set_index(data.filename)
dist_df.columns = data.filename
dist_df| filename | blues.00000.wav | blues.00001.wav | blues.00002.wav | blues.00003.wav | blues.00004.wav | blues.00005.wav | blues.00006.wav | blues.00007.wav | blues.00008.wav | blues.00009.wav | ... | rock.00090.wav | rock.00091.wav | rock.00092.wav | rock.00093.wav | rock.00094.wav | rock.00095.wav | rock.00096.wav | rock.00097.wav | rock.00098.wav | rock.00099.wav |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| filename | |||||||||||||||||||||
| blues.00000.wav | 0.000000 | 0.950769 | 0.410382 | 0.715138 | 0.974439 | 1.346688 | 1.219483 | 1.167626 | 0.358123 | 1.097889 | ... | 1.082829 | 0.453831 | 0.421442 | 0.337410 | 0.428371 | 0.389058 | 0.359165 | 0.503706 | 7.150424e-01 | 0.695902 |
| blues.00001.wav | 0.950769 | 0.000000 | 1.096834 | 0.479097 | 0.919251 | 0.692144 | 0.681714 | 0.584742 | 0.879351 | 0.595832 | ... | 1.098111 | 1.325126 | 1.370792 | 1.191698 | 1.330834 | 1.077301 | 1.222119 | 1.302573 | 5.004378e-01 | 0.688277 |
| blues.00002.wav | 0.410382 | 1.096834 | 0.000000 | 0.789589 | 0.599734 | 1.082019 | 1.028061 | 0.895554 | 0.531887 | 1.132532 | ... | 1.032408 | 0.438926 | 0.409221 | 0.416707 | 0.485463 | 0.504293 | 0.433163 | 0.410017 | 7.836224e-01 | 0.678931 |
| blues.00003.wav | 0.715138 | 0.479097 | 0.789589 | 0.000000 | 0.873563 | 0.865204 | 0.699254 | 0.675434 | 0.647242 | 0.704816 | ... | 1.320107 | 1.206516 | 1.151132 | 0.958014 | 1.172515 | 1.000287 | 0.979485 | 1.107821 | 4.977214e-01 | 0.816790 |
| blues.00004.wav | 0.974439 | 0.919251 | 0.599734 | 0.873563 | 0.000000 | 0.443934 | 0.517805 | 0.376545 | 0.970297 | 0.528343 | ... | 0.912395 | 0.982634 | 0.861965 | 0.895316 | 1.034594 | 0.936546 | 0.936454 | 0.827056 | 8.468076e-01 | 0.938215 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| rock.00095.wav | 0.389058 | 1.077301 | 0.504293 | 1.000287 | 0.936546 | 1.231012 | 1.251086 | 1.180628 | 0.380550 | 1.258421 | ... | 0.976074 | 0.156273 | 0.148274 | 0.139406 | 0.148544 | 0.000000 | 0.097936 | 0.134759 | 6.501304e-01 | 0.477517 |
| rock.00096.wav | 0.359165 | 1.222119 | 0.433163 | 0.979485 | 0.936454 | 1.272209 | 1.291506 | 1.256171 | 0.387830 | 1.304636 | ... | 1.033826 | 0.128774 | 0.117175 | 0.119638 | 0.136938 | 0.097936 | 0.000000 | 0.097929 | 6.621657e-01 | 0.527669 |
| rock.00097.wav | 0.503706 | 1.302573 | 0.410017 | 1.107821 | 0.827056 | 1.175960 | 1.203014 | 1.155732 | 0.538680 | 1.330066 | ... | 1.008336 | 0.085830 | 0.073442 | 0.102346 | 0.121149 | 0.134759 | 0.097929 | 0.000000 | 7.128432e-01 | 0.584827 |
| rock.00098.wav | 0.715042 | 0.500438 | 0.783622 | 0.497721 | 0.846808 | 0.823649 | 0.747662 | 0.760559 | 0.545327 | 0.770608 | ... | 1.233309 | 0.882145 | 0.838117 | 0.672067 | 0.842023 | 0.650130 | 0.662166 | 0.712843 | 1.110223e-16 | 0.410959 |
| rock.00099.wav | 0.695902 | 0.688277 | 0.678931 | 0.816790 | 0.938215 | 0.947553 | 0.934447 | 0.931351 | 0.596138 | 0.916584 | ... | 0.967935 | 0.669430 | 0.742470 | 0.564428 | 0.670396 | 0.477517 | 0.527669 | 0.584827 | 4.109593e-01 | 0.000000 |
1000 rows × 1000 columns
def dis_aud(path, duration=5):
"display audio"
x,sr=librosa.load(path, duration=duration)
ipd.display(ipd.Audio(x,rate=sr))Example 1. Beethoven Piano Sonata No.8
song_name = 'classical.00077.wav'
dis_aud('data/GTZAN_data/genres_original/'+song_name.split('.')[0]+'/'+song_name, 10)- 노래:
- Beethoven Piano sonata no.8, I. Grave by Barenboim
sim_songs = songs_similarity(song_name, features_scaled)sim_songs.sort_values(ascending = True)filename
classical.00079.wav 0.163032
classical.00065.wav 0.192554
classical.00035.wav 0.232339
classical.00066.wav 0.266772
classical.00058.wav 0.272568
...
metal.00053.wav 1.609674
metal.00051.wav 1.623948
metal.00087.wav 1.627464
disco.00003.wav 1.628881
metal.00078.wav 1.676451
Name: classical.00077.wav, Length: 999, dtype: float64
print('- 3 most similar songs: -')
for i in range(3):
sim_song = sim_songs.index[i]
print(sim_song)
dis_aud('data/GTZAN_data/genres_original/'+sim_song.split('.')[0]+'/'+ sim_song, 5)- 3 most similar songs: -
classical.00079.wav
classical.00065.wav
classical.00035.wav
가장 가까운 노래 3개
- classical.00079.wav
- Beethoven Piano sonata no.8, in c minor III. Rondo. Allegro by Horowitz
- 같은 작곡가의 같은 음악 작품이지만, 다른 악장이며 연주자가 다르다
- classical.00065.wav
- Schubert Piano sonata no.21 in b-flat III. Scherzo by A. Brendel
- 같은 피아노 소나타 곡이지만 다른 작곡가이다 (다만 비슷한 시기 (classical-romantic))
- classical.00035.wav
- Ravel Quartet in f major for strings: II
- 같은 클래식 장르이다.
print('- 3 most different songs: -')
for i in range(1,4):
sim_song = sim_songs.index[-i]
print(sim_song)
dis_aud('data/GTZAN_data/genres_original/'+sim_song.split('.')[0]+'/'+ sim_song, 5)- 3 most different songs: -
metal.00078.wav
disco.00003.wav
metal.00087.wav
가장 먼 노래 3개
- metal.00078.wav
- Metallica - Prince Charming
- 헤비메탈 곡이다. 이해가 된다..
- disco.00003.wav
- Don Armando’s 2nd Ave. Rhumba Band - Deputy of Love
- 훵키한 디스코 곡이나 베이스와 드럼이 두드러진다.
- metal.00087.wav
- Metallica - So What
- 또다시 메탈리카..
Example 2. Britney Spears - Lucky
song_name = 'pop.00030.wav'
dis_aud('data/GTZAN_data/genres_original/'+song_name.split('.')[0]+'/'+song_name, 10)- 노래:
- Britney Spears - Lucky
sim_songs = songs_similarity(song_name, features_scaled)print('- 3 most similar songs: -')
for i in range(3):
sim_song = sim_songs.index[i]
print(sim_song)
dis_aud('data/GTZAN_data/genres_original/'+sim_song.split('.')[0]+'/'+ sim_song, 5)- 3 most similar songs: -
pop.00031.wav
pop.00020.wav
pop.00035.wav
가장 가까운 노래 3곡
- pop.00031.wav
- 같은 노래이다 (GTZAN 데이터에 같은 노래가 있다!)
- pop.00020.wav
- Britney Spears - Can’t Make You Love Me
- 같은 가수 브리트니 스피어스다.
- pop.00035.wav
- Britney Spears - Stronger
- “It’s Britney, again”
print('- 3 most different songs: -')
for i in range(1,4):
sim_song = sim_songs.index[-i]
print(sim_song)
dis_aud('data/GTZAN_data/genres_original/'+sim_song.split('.')[0]+'/'+ sim_song, 5)- 3 most different songs: -
rock.00099.wav
country.00086.wav
country.00050.wav
가장 먼 노래 3곡
- rock.00099.wav
- The Stone Roses - This Is the One
- 얼터너비트 록 밴드 The Stone Roses의 노래이다. 팝과 거리가 멀다고 할 수 있겠다.
- country.00086.wav
- Brad Paisley - It Never Woulda Worked Out Anyway
- 컨트리 송이다. 꽤나 잔잔하다.
- country.00050.wav
- Vince Gill - Never Alone
- 또, 컨트리 송이다.
Example 3. A Tribe Called Quest - Electric Relaxation
song_name = 'hiphop.00050.wav'
dis_aud('data/GTZAN_data/genres_original/'+song_name.split('.')[0]+'/'+song_name, 10)- 노래:
- A Tribe Called Quest - Electric Relaxation
sim_songs = songs_similarity(song_name, features_scaled)print('- 3 most similar songs: -')
for i in range(3):
sim_song = sim_songs.index[i]
print(sim_song)
dis_aud('data/GTZAN_data/genres_original/'+sim_song.split('.')[0]+'/'+ sim_song, 5)- 3 most similar songs: -
hiphop.00072.wav
reggae.00065.wav
hiphop.00073.wav
가장 가까운 노래 3곡
- hiphop.00072.wav
- 같은 노래의 다른 부분이다.
- reggae.00065.wav
- Dennis Brown - Rub a Dub All the Time
- 레게 노래이다. 힙합과 가까운 장르이다. 드럼이 유사하다.
- hiphop.00073.wav
- A Tribe Called Quest - Jazz (we’ve got)
- 같은 밴드 ATCQ이다.
print('- 3 most different songs: -')
for i in range(1,4):
sim_song = sim_songs.index[-i]
print(sim_song)
dis_aud('data/GTZAN_data/genres_original/'+sim_song.split('.')[0]+'/'+ sim_song, 5)- 3 most different songs: -
rock.00080.wav
rock.00082.wav
jazz.00021.wav
가장 먼 노래 3곡
- rock.00080.wav
- Survivor - Is This Love
- 팝 락이다.
- rock.00082.wav
- Survivor - Burning Heart
- 마찬가지이다.
- jazz.00021.wav
- Joe Lovano - Uprising
- ATCQ와 재즈가 멀리 있다는 결과가 아쉽지만, 목소리의 부재 드럼패턴 등 그럴만한 이유가 있어보인다.
유클리드 거리 기반 (Based on euclidean distance)
Example 1. Beethoven Piano Sonata No.8
song_name = 'classical.00077.wav'
sim_songs = songs_similarity(song_name, features_scaled, metric='euclidean')print('- 3 most similar songs: -')
for i in range(3):
sim_song = sim_songs.index[i]
print(sim_song)
# ipd.display(ipd.Audio('data/GTZAN_data/genres_original/'+sim_song.split('.')[0]+'/'+ sim_song))- 3 most similar songs: -
classical.00079.wav
classical.00065.wav
classical.00035.wav
- 코사인 거리 기반과 동일하다 (오디오 디스플레이는 생략한다.)
print('- 3 most different songs: -')
for i in range(1,4):
sim_song = sim_songs.index[-i]
print(sim_song)
dis_aud('data/GTZAN_data/genres_original/'+sim_song.split('.')[0]+'/'+ sim_song, 10)- 3 most different songs: -
classical.00089.wav
pop.00053.wav
reggae.00086.wav
- classical.00089.wav
- Vivaldi The Four Seasons: Violin Concerto no.1’spring’
- 같은 클래식 곡이지만 바이올린 소리만 포함한다.
- pop.00053.wav
- Destiny’s Child - Outro
- 아카펠라 버전의 팝송이다!
- reggae.00086.wav
- 깨진 파일이다.
Example 2. Britney Spears - Lucky
song_name = 'pop.00030.wav'
sim_songs = songs_similarity(song_name, features_scaled, metric='euclidean')print('- 3 most similar songs: -')
for i in range(3):
sim_song = sim_songs.index[i]
print(sim_song)
# ipd.display(ipd.Audio('data/GTZAN_data/genres_original/'+sim_song.split('.')[0]+'/'+ sim_song))- 3 most similar songs: -
pop.00031.wav
pop.00020.wav
pop.00035.wav
- 역시 코사인 거리 기반 측정과 같은 결과이다.
print('- 3 most different songs: -')
for i in range(1,4):
sim_song = sim_songs.index[-i]
print(sim_song)
dis_aud('data/GTZAN_data/genres_original/'+sim_song.split('.')[0]+'/'+ sim_song, 5)- 3 most different songs: -
classical.00089.wav
pop.00053.wav
jazz.00007.wav
위 두곡은 Example 1과 같은 결과이며
jazz.0007.wav
- 잔잔한 재즈 곡이다.
Example 3. A Tribe Called Quest - Electric Relaxation
song_name = 'hiphop.00050.wav'
sim_songs = songs_similarity(song_name, features_scaled, metric='euclidean')print('- 3 most similar songs: -')
for i in range(3):
sim_song = sim_songs.index[i]
print(sim_song)
dis_aud('data/GTZAN_data/genres_original/'+sim_song.split('.')[0]+'/'+ sim_song, 5)- 3 most similar songs: -
hiphop.00072.wav
reggae.00065.wav
hiphop.00066.wav
위 두곡은 cosine 거리 결과와 같다.
hiphop.00066.wav
- ATCQ - Bonita Applebum (same artist)
print('- 3 most different songs: -')
for i in range(1,4):
sim_song = sim_songs.index[-i]
print(sim_song)
#ipd.display(ipd.Audio('data/GTZAN_data/genres_original/'+sim_song.split('.')[0]+'/'+ sim_song))- 3 most different songs: -
classical.00089.wav
pop.00053.wav
jazz.00007.wav
- example 2와 같다.
유클리드 거리 기반으로 측정하면 가장 먼 곡이 한 악기나 보컬로만 구성되거나 소음만 있는 음악이다.